package com.fh.shop.api.interceptor;

import com.alibaba.fastjson.JSON;
import com.fh.shop.api.annotation.NeedLogin;
import com.fh.shop.api.common.ResponseEnum;
import com.fh.shop.api.common.SystemConst;
import com.fh.shop.api.exception.TokenException;
import com.fh.shop.api.member.vo.MemberVo;
import com.fh.shop.api.util.KeyUtil;
import com.fh.shop.api.util.Md5Util;
import com.fh.shop.api.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Base64;

public class LoginInterceptor extends HandlerInterceptorAdapter {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //解决跨域   * -》 代表所有的客户端都可以访问
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"*");
        //允许客户端传递 自定义的头信息
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"x-auth,content-type,x-idempotence");
        //允许客户端发送请求方式的类型 【默认情况下 grt,post 请求是没问题的，但对于其他请求类型需要特别允许】
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"DELETE,PUT");
        //options请求并不是真正的请求，对于options请求，拦截 return false
        String requestMethod = request.getMethod();
        //获取请求的方式 -- 判断并忽略大小写
        if (requestMethod.equalsIgnoreCase("options")){
            return false;
        }
        //如果 handler这个对象 不是 HandlerMethod类 的实例化对象 - 直接放行 --  处理 swagger html 静态页面
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        //获取方法
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //判断方法是否有注解
        if (!method.isAnnotationPresent(NeedLogin.class)){
            //没有注解直接放行
            return true;
        }

        //进行验证
        String header = request.getHeader("x-auth");
        //非空验证
        if (StringUtils.isEmpty(header)){
            throw new TokenException(ResponseEnum.TOKEN_IS_NULL);
        }
        //验证格式 x.y --  .在java中有特殊含义，要 \\转义
        String[] headers = header.split("\\.");
        if (headers.length != 2){
            throw new TokenException(ResponseEnum.TOKEN_FORMAT_IS_ERROR);
        }
        //验签
        String Base64MemberJSON = headers[0];
        String base64Sign = headers[1];
        //解码 Base64偏码    字节数组转字符串  new String（字节数组）
        String memberJSON = null;
        String sign = null;
        try {
            memberJSON = new String(Base64.getDecoder().decode(Base64MemberJSON));
            sign = new String(Base64.getDecoder().decode(base64Sign));
        } catch (Exception e) {
            e.printStackTrace();
            throw new TokenException(ResponseEnum.TOKEN_DECODE_FAIL);
        }
        //验证  数据+秘钥=签名 对比判断
        String newSign = Md5Util.sign(memberJSON, SystemConst.SECRET);
        if (!newSign.equals(sign)){
            throw new TokenException(ResponseEnum.TOKEN_VALIDATE_IS_ERROR);
        }
        //转java对象
        MemberVo memberVo = JSON.parseObject(memberJSON, MemberVo.class);
        Long id = memberVo.getId();
        //超时判断
        String key = KeyUtil.buildMemberKey(id);
        if (!RedisUtil.exist(key)){
            throw new TokenException(ResponseEnum.TOKEN_FORMAT_IS_NULL);
        }
        //uuid是否是最新生成的uuid 是否一致
        String uuid = memberVo.getUuid();  // 客户端传输过来的
        String redisUUID = RedisUtil.get(key);  // redis中存储
        //判断传过来的uuid 和 存入redis中的uuid 是否一致
        if (!uuid.equals(redisUUID)){
            throw new TokenException(ResponseEnum.TOKEN_IS_MISMATH);
        }
        //超时，时间重置
        RedisUtil.expire(key,SystemConst.MEMBER_EXPIRE_TIME);
        //将用户信息存到request,方便后续使用
        request.setAttribute(SystemConst.CURR_MEMBER,memberVo);

        return true;
    }

}
